package com.ifonly.common.typeconverter.impl;

import com.ifonly.common.typeconverter.Converter;
import com.ifonly.common.typeconverter.TypeConvert;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author <a href="mailto:ifonlymaster@163.com">ifonly</a>
 * @version 1.0 2015-12-18 09:15
 * @since JDK 1.6
 */
public class ArrayConverter<T> implements Converter<T[]> {

    protected final Class<T> targetComponentType;

    public ArrayConverter(Class<T> targetComponentType) {
        this.targetComponentType = targetComponentType;
    }

    public T[] convert(Object value) {
        if (value == null) {
            return null;
        }

        Class valueClass = value.getClass();

        if (!valueClass.isArray()) {
            // source is not an array
            return convertValueToArray(value);
        }

        // source is an array
        return convertArrayToArray(value);
    }


    protected T convertType(Object value) {
        return TypeConvert.convert(targetComponentType, value);
    }

    @SuppressWarnings("unchecked")
    protected T[] createArray(int length) {
        return (T[]) Array.newInstance(targetComponentType, length);
    }

    protected T[] convertToSingleElementArray(Object value) {
        T[] singleElementArray = createArray(1);
        singleElementArray[0] = convertType(value);
        return singleElementArray;
    }

    protected T[] convertValueToArray(Object value) {
        if (value instanceof List) {
            List list = (List) value;
            T[] target = createArray(list.size());

            for (int i = 0; i < list.size(); i++) {
                Object element = list.get(i);
                target[i] = convertType(element);
            }

            return target;
        }

        if (value instanceof Collection) {
            Collection collection = (Collection) value;
            T[] target = createArray(collection.size());

            int i = 0;
            for (Object element : collection) {
                target[i] = convertType(element);
                i++;
            }

            return target;
        }

        if (value instanceof Iterable) {
            Iterable iterable = (Iterable) value;
            List<T> list = new ArrayList<T>();

            for (Object element : iterable) {
                list.add(convertType(element));
            }

            T[] target = createArray(list.size());
            return list.toArray(target);
        }

        return convertToSingleElementArray(value);
    }

    @SuppressWarnings("unchecked")
    protected T[] convertArrayToArray(Object value) {
        Class valueComponentType = value.getClass().getComponentType();

        if (valueComponentType == targetComponentType) {
            // equal types, no conversion needed
            return (T[]) value;
        }

        T[] result;

        if (valueComponentType.isPrimitive()) {
            // convert primitive array to target array
            result = convertPrimitiveArrayToArray(value, valueComponentType);
        } else {
            // convert object array to target array
            Object[] array = (Object[]) value;
            result = convertObjectArrayToArray(array);
        }

        return result;
    }

    private T[] convertObjectArrayToArray(Object[] array) {
        T[] result = createArray(array.length);
        for (int i = 0; i < array.length; i++) {
            result[i] = convertType(array[i]);
        }
        return result;
    }

    @SuppressWarnings("AutoBoxing")
    protected T[] convertPrimitiveArrayToArray(Object value, Class primitiveComponentType) {
        T[] result = null;

        if (primitiveComponentType == int.class) {
            int[] array = (int[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        } else if (primitiveComponentType == long.class) {
            long[] array = (long[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        } else if (primitiveComponentType == float.class) {
            float[] array = (float[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        } else if (primitiveComponentType == double.class) {
            double[] array = (double[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        } else if (primitiveComponentType == short.class) {
            short[] array = (short[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        } else if (primitiveComponentType == byte.class) {
            byte[] array = (byte[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        } else if (primitiveComponentType == char.class) {
            char[] array = (char[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        } else if (primitiveComponentType == boolean.class) {
            boolean[] array = (boolean[]) value;
            result = createArray(array.length);
            for (int i = 0; i < array.length; i++) {
                result[i] = convertType(array[i]);
            }
        }
        return result;
    }

}
